package match

import (
	"context"
	"github.com/nuttech/bell"
	"github.com/stretchr/testify/assert"
	"gitverse.ru/synapse/kubelatte/pkg/api/common"
	"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/observability/logs"
	"gitverse.ru/synapse/kubelatte/pkg/storage"
	"go.uber.org/zap"
	"k8s.io/api/admission/v1beta1"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"testing"
)

func TestMatchCompatibility(t *testing.T) {
	type args struct {
		data  common.ARFields
		match v1alpha1.Match
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "full_match",
			args: args{
				data: common.ARFields{
					Kind: v1.GroupVersionKind{
						Group:   "ApiGroup",
						Version: "v1",
						Kind:    "Pod",
					},
					Namespace: "incl_ns",
					UserInfo:  common.ARUserInfo{Username: "kate"},
					Operation: v1beta1.Create,
					OldObject: nil,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":        "some_name",
							"namespace":   "incl_ns",
							"annotations": map[string]interface{}{"annotNS/annotTr": Enabled},
							"labels":      map[string]interface{}{"key": "val"},
						},
					},
				},
				match: v1alpha1.Match{
					Kinds: []v1alpha1.Kind{
						{
							Kind:      []string{"Pod"},
							ApiGroups: []string{"ApiGroup"},
						},
					},
					UserInfo: v1alpha1.UserInfo{Username: "kate"},
					Name: v1alpha1.Name{
						Value: "some_name",
					},
					Scope: scopeNamespaced,
					NamespaceSelector: v1alpha1.NamespaceSelector{MatchExpressions: []v1alpha1.MatchExpression{
						{
							Key:      "matching Key",
							Operator: string(In),
							Values:   []string{"matching Val"},
						}, {
							Key:      "matching Key",
							Operator: string(NotIn),
							Values:   []string{"mismatching Val"},
						}, {

							Key:      "matching Key",
							Operator: string(Exists),
						}, {
							Key:      "Another key",
							Operator: string(DoesNotExist),
						}}},
					ExcludedNamespaces: v1alpha1.Namespace{
						Values: []string{"excl_ns"},
					},
					IncludedNamespaces: v1alpha1.Namespace{
						Values: []string{"incl_ns"},
					},
					Operations: []string{"CREATE"},
					AnnotationSelector: v1alpha1.AnnotationSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "annotNS/annotTr",
								Operator: "In",
								Values:   []string{Enabled},
							},
						},
					},
					LabelSelector: v1alpha1.LabelSelector{
						MatchExpressions: []v1alpha1.MatchExpression{{
							Key:      "key",
							Operator: string(In),
							Values:   []string{"val"},
						}, {
							Key:      "Key",
							Operator: string(NotIn),
							Values:   []string{"another Val"},
						}, {

							Key:      "key",
							Operator: string(Exists),
						}, {
							Key:      "Another key",
							Operator: string(DoesNotExist),
						},
						},
					},
				},
			},
			want: true,
		},
		{
			name: "not matched",
			args: args{
				data: common.ARFields{
					Kind: v1.GroupVersionKind{
						Group:   "ApiGroup",
						Version: "v1",
						Kind:    "Deployment",
					},
					Namespace: "incl_ns",
					UserInfo:  common.ARUserInfo{Username: "kate"},
					Operation: v1beta1.Create,
					OldObject: nil,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":        "some_name",
							"namespace":   "incl_ns",
							"annotations": map[string]interface{}{"annotNS/annotTr": Enabled},
							"labels":      map[string]interface{}{"key": "val"},
						},
					},
				},
				match: v1alpha1.Match{
					Kinds: []v1alpha1.Kind{
						{
							Kind:      []string{"Pod"},
							ApiGroups: []string{"ApiGroup"},
						},
					},
					UserInfo: v1alpha1.UserInfo{Username: "kate"},
					Name: v1alpha1.Name{
						Value: "some_name",
					},
					Scope:             scopeNamespaced,
					NamespaceSelector: v1alpha1.NamespaceSelector{MatchExpressions: []v1alpha1.MatchExpression{}},
					ExcludedNamespaces: v1alpha1.Namespace{
						Values: []string{"excl_ns"},
					},
					IncludedNamespaces: v1alpha1.Namespace{
						Values: []string{"incl_ns"},
					},
					Operations: []string{"CREATE"},
					AnnotationSelector: v1alpha1.AnnotationSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "annotNS/annotTr",
								Operator: "In",
								Values:   []string{Enabled},
							},
						},
					},
					LabelSelector: v1alpha1.LabelSelector{
						MatchExpressions: []v1alpha1.MatchExpression{{
							Key:      "key",
							Operator: "In",
							Values:   []string{"val"},
						}},
					},
				},
			},
			want: false,
		},
		{
			name: "empty match section",
			args: args{
				data: common.ARFields{
					Namespace: "incl_ns",
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{},
			},
			want: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			storage.Storage = &storage.StorageController{}
			storage.Storage.Start(false, false)
			storage.Storage.UpdateNamespace(bell.Message{
				Value: corev1.Namespace{
					TypeMeta: v1.TypeMeta{},
					ObjectMeta: v1.ObjectMeta{
						Namespace: "incl_ns",
						Name:      "incl_ns",
						Labels:    map[string]string{"matching Key": "matching Val"},
					},
					Spec:   corev1.NamespaceSpec{},
					Status: corev1.NamespaceStatus{},
				}})
			if got := CheckMatching(context.Background(), tt.args.data, tt.args.match); got != tt.want {
				t.Errorf("CheckMatching() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_doesAnnotationSelectorMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "match annotations",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
							"annotations": map[string]interface{}{
								"kblt/key": Enabled,
							},
						},
					},
				},
				match: v1alpha1.Match{
					AnnotationSelector: v1alpha1.AnnotationSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "kblt/key",
								Operator: "In",
								Values:   []string{Enabled, "true"},
							},
						},
					},
				},
			},
			wantMatches: true,
			wantReason:  "annotation selector",
		},
		{
			name: "empty match section",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
							"annotations": map[string]interface{}{
								"kblt/key": Enabled,
							},
						},
					},
				},
				match: v1alpha1.Match{
					AnnotationSelector: v1alpha1.AnnotationSelector{},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "wrong annotations format",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
							"annotations": map[bool]interface{}{
								true: Enabled,
							},
						},
					},
				},
				match: v1alpha1.Match{
					AnnotationSelector: v1alpha1.AnnotationSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "kblt/key",
								Operator: "In",
								Values:   []string{Enabled, "true"},
							},
						},
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.metadata.annotations",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			gotMatches, gotReason := doesAnnotationSelectorMatches(context.Background(), tt.args.fields, tt.args.match)
			if gotMatches != tt.wantMatches {
				t.Errorf("doesAnnotationSelectorMatches() gotMatches = %v, want %v", gotMatches, tt.wantMatches)
			}
			if gotReason != tt.wantReason {
				t.Errorf("doesAnnotationSelectorMatches() gotReason = %v, want %v", gotReason, tt.wantReason)
			}
		})
	}
}

func Test_doesIncludeExcludeNssMatch(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		{
			name: "included match",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "kblt",
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{},
					},
				},
				match: v1alpha1.Match{
					IncludedNamespaces: v1alpha1.Namespace{
						Values: []string{"kblt"},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "included not match",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "kblt",
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{},
					},
				},
				match: v1alpha1.Match{
					IncludedNamespaces: v1alpha1.Namespace{
						Values: []string{"test"},
					},
				},
			},
			want:  false,
			want1: "included namespace value",
		},
		{
			name: "included empty section match",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "kblt",
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{},
					},
				},
				match: v1alpha1.Match{
					IncludedNamespaces: v1alpha1.Namespace{},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "included regex match",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "kblt",
				},
				match: v1alpha1.Match{
					IncludedNamespaces: v1alpha1.Namespace{
						Regex: []string{"kbl.*"},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "included regex not match",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "test",
				},
				match: v1alpha1.Match{
					IncludedNamespaces: v1alpha1.Namespace{
						Regex: []string{"kbl.*"},
					},
				},
			},
			want:  false,
			want1: "included namespace regex",
		},
		{
			name: "included wrong regex",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "kblt",
				},
				match: v1alpha1.Match{
					IncludedNamespaces: v1alpha1.Namespace{
						Regex: []string{"a(b"},
					},
				},
			},
			want:  false,
			want1: "namespace invalid regex",
		},
		{
			name: "excluded regex not match",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Namespace: "kblt",
				},
				match: v1alpha1.Match{
					ExcludedNamespaces: v1alpha1.Namespace{
						Regex: []string{"kbl.*"},
					},
				},
			},
			want:  false,
			want1: "excluded namespace regex",
		},
		{
			name: "empty namespace admission review",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{
					ExcludedNamespaces: v1alpha1.Namespace{
						Regex: []string{"kbl.*"},
					},
				},
			},
			want:  true,
			want1: "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			got, got1 := doesIncludeExcludeNssMatch(context.Background(), tt.args.fields, tt.args.match)
			if got != tt.want {
				t.Errorf("doesIncludeExcludeNssMatch() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("doesIncludeExcludeNssMatch() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_doesKindsMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "empty match kind",
			args: args{
				fields: common.ARFields{
					Kind: v1.GroupVersionKind{
						Group:   "",
						Version: "v1",
						Kind:    "Pod",
					},
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{
					Kinds: []v1alpha1.Kind{},
				},
			},
			wantMatches: false,
			wantReason:  "request.kind.group",
		},
		{
			name: "match kind and version",
			args: args{
				fields: common.ARFields{
					Kind: v1.GroupVersionKind{
						Group:   "",
						Version: "v1",
						Kind:    "Pod",
					},
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{
					Kinds: []v1alpha1.Kind{
						{
							Kind:      []string{"Pod"},
							ApiGroups: []string{""},
						},
					},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "match kind and not match version",
			args: args{
				fields: common.ARFields{
					Kind: v1.GroupVersionKind{
						Group:   "2",
						Version: "v1",
						Kind:    "Pod",
					},
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{
					Kinds: []v1alpha1.Kind{
						{
							Kind:      []string{"Pod"},
							ApiGroups: []string{"3"},
						},
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.kind.group",
		},
		{
			name: "match version and not match kind",
			args: args{
				fields: common.ARFields{
					Kind: v1.GroupVersionKind{
						Group:   "3",
						Version: "v1",
						Kind:    "Deployment",
					},
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{
					Kinds: []v1alpha1.Kind{
						{
							Kind:      []string{"Pod"},
							ApiGroups: []string{"3"},
						},
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.kind.kind",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotMatches, gotReason := doesKindsMatches(context.Background(), tt.args.fields, tt.args.match)
			if gotMatches != tt.wantMatches {
				t.Errorf("doesKindsMatches() gotMatches = %v, want %v", gotMatches, tt.wantMatches)
			}
			if gotReason != tt.wantReason {
				t.Errorf("doesKindsMatches() gotReason = %v, want %v", gotReason, tt.wantReason)
			}
		})
	}
}

func Test_doesLabelSelectorMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "empty match.labelSelector",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					LabelSelector: v1alpha1.LabelSelector{},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "wrong labels format",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
							"labels": map[bool]interface{}{
								true: map[string]interface{}{
									"test": map[string]interface{}{
										"test": "test",
									},
								},
							},
						},
					},
				},
				match: v1alpha1.Match{
					LabelSelector: v1alpha1.LabelSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "key",
								Operator: "In",
								Values:   []string{"value"},
							},
						},
					},
				},
			},
			wantMatches: false,
			wantReason:  "wrong metadata.labels format",
		},
		{
			name: "match labels",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
							"labels": map[string]interface{}{
								"key": "value",
							},
						},
					},
				},
				match: v1alpha1.Match{
					LabelSelector: v1alpha1.LabelSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "key",
								Operator: "In",
								Values:   []string{"value", "not-value"},
							},
						},
					},
				},
			},
			wantMatches: true,
			wantReason:  "metadata.labels",
		},
		{
			name: "not matched labels",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
							"labels": map[string]interface{}{
								"key": "value",
							},
						},
					},
				},
				match: v1alpha1.Match{
					LabelSelector: v1alpha1.LabelSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "key",
								Operator: "NotIn",
								Values:   []string{"value", "not-value"},
							},
						},
					},
				},
			},
			wantMatches: false,
			wantReason:  "metadata.labels",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			gotMatches, gotReason := doesLabelSelectorMatches(context.Background(), tt.args.fields, tt.args.match)
			if gotMatches != tt.wantMatches {
				t.Errorf("doesLabelSelectorMatches() gotMatches = %v, want %v", gotMatches, tt.wantMatches)
			}
			if gotReason != tt.wantReason {
				t.Errorf("doesLabelSelectorMatches() gotReason = %v, want %v", gotReason, tt.wantReason)
			}
		})
	}
}

func Test_doesMatchConditionsMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		// Create
		{
			name: "empty match section",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "object mus be matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						Object: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "object mus be matched mustNotExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						Object: []v1alpha1.Obj{{
							Path:      "metadata.annotations",
							Condition: mustNotExist,
						}},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "object mus be not matched mustNotExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						Object: []v1alpha1.Obj{{
							Path:      "metadata.annotations",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "object mus be not matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						Object: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustNotExist,
						}},
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		// Delete
		{
			name: "OldObject mus be matched mustNotExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Delete,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.annotations",
							Condition: mustNotExist,
						}},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "OldObject mus be matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Delete,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "OldObject mus be not matched mustNotExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Delete,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustNotExist,
						}},
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "OldObject mus be not matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Delete,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.annotations",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		// Update
		{
			name: "both must be matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Update,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustExist,
						}},
						Object: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "both: Object must be not matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Update,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustExist,
						}},
						Object: []v1alpha1.Obj{{
							Path:      "metadata.annotations",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "both: OldObject must be not matched mustExist",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Update,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					MatchConditions: v1alpha1.MatchConditions{
						OldObject: []v1alpha1.Obj{{
							Path:      "metadata.annotations",
							Condition: mustExist,
						}},
						Object: []v1alpha1.Obj{{
							Path:      "metadata.name",
							Condition: mustExist,
						}},
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := doesMatchConditionsMatches(context.Background(), tt.args.fields, tt.args.match)
			if got != tt.want {
				t.Errorf("doesMatchConditionsMatches() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("doesMatchConditionsMatches() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_doesNameMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "without metadata.name",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"namespace":   "incl_ns",
							"annotations": map[string]interface{}{"annotNS/annotTr": Enabled},
							"labels":      map[string]interface{}{"key": "val"},
						},
					},
				},
				match: v1alpha1.Match{
					Name: v1alpha1.Name{
						Value: "some_name",
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.oldObject.metadata.name",
		},
		{
			name: "empty match name",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"namespace": "incl_ns",
							"name":      "incl_ns",
						},
					},
				},
				match: v1alpha1.Match{
					Name: v1alpha1.Name{
						Value: "",
						Regex: "",
					},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "match name value",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name": "test-nane-value",
						},
					},
				},
				match: v1alpha1.Match{
					Name: v1alpha1.Name{
						Value: "test-nane-value",
					},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "match name regex",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name": "test-name-value",
						},
					},
				},
				match: v1alpha1.Match{
					Name: v1alpha1.Name{
						Regex: ".*-name-.*",
					},
				},
			},
			wantMatches: true,
			wantReason:  "request.oldObject.metadata.name",
		},
		{
			name: "not matched name regex",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name": "test-name-value",
						},
					},
				},
				match: v1alpha1.Match{
					Name: v1alpha1.Name{
						Regex: ".*-nane-.*",
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.oldObject.metadata.name",
		},
		{
			name: "wrong name regex",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name": "test-name-value",
						},
					},
				},
				match: v1alpha1.Match{
					Name: v1alpha1.Name{
						Regex: `a(b`,
					},
				},
			},
			wantMatches: false,
			wantReason:  "wrong match.name.regex: error parsing regexp: missing closing ): `a(b`",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotMatches, gotReason := doesNameMatches(context.Background(), tt.args.fields, tt.args.match)
			assert.Equalf(t, tt.wantMatches, gotMatches, "doesNameMatches(%v, %v)", tt.args.fields, tt.args.match)
			assert.Equalf(t, tt.wantReason, gotReason, "doesNameMatches(%v, %v)", tt.args.fields, tt.args.match)
		})
	}
}

func Test_doesNamespaceSelectorMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		{
			name: "empty match section",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Update,
					OldObject: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					NamespaceSelector: v1alpha1.NamespaceSelector{},
				},
			},
			want:  true,
			want1: "namespace selector",
		},
		{
			name: "cluster objet",
			args: args{
				fields: common.ARFields{
					Namespace: "",
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					NamespaceSelector: v1alpha1.NamespaceSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "matchingKey",
								Operator: "In",
								Values:   []string{"matchingVal"},
							},
						},
					},
				},
			},
			want:  true,
			want1: "namespace selector",
		},
		{
			name: "match namespace",
			args: args{
				fields: common.ARFields{
					Namespace: "ns-test",
					Operation: v1beta1.Create,
					Object: map[string]interface{}{
						"metadata": map[string]interface{}{
							"name":         "name-obj",
							"generateName": "generateName-obj",
						},
					},
				},
				match: v1alpha1.Match{
					NamespaceSelector: v1alpha1.NamespaceSelector{
						MatchExpressions: []v1alpha1.MatchExpression{
							{
								Key:      "different-ns",
								Operator: "In",
								Values:   []string{"matchingVal"},
							},
						},
					},
				},
			},
			want:  false,
			want1: "namespace selector",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			storage.Storage = &storage.StorageController{}
			storage.Storage.Start(false, false)
			storage.Storage.UpdateNamespace(bell.Message{
				Value: corev1.Namespace{
					TypeMeta: v1.TypeMeta{},
					ObjectMeta: v1.ObjectMeta{
						Namespace: "incl_ns",
						Name:      "incl_ns",
						Labels:    map[string]string{"matchingKey": "matchingVal"},
					},
					Spec:   corev1.NamespaceSpec{},
					Status: corev1.NamespaceStatus{},
				}})
			got, got1 := doesNamespaceSelectorMatches(context.Background(), tt.args.fields, tt.args.match)
			if got != tt.want {
				t.Errorf("doesNamespaceSelectorMatches() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("doesNamespaceSelectorMatches() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_doesNsNamesMatch(t *testing.T) {
	type args struct {
		matchNs v1alpha1.Namespace
		ns      string
		include bool
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		{
			name: "include namespace value must be matched",
			args: args{
				matchNs: v1alpha1.Namespace{
					Values: []string{"kblt-test"},
				},
				ns:      "kblt-test",
				include: true,
			},
			want:  true,
			want1: "included namespace value",
		},
		{
			name: "include namespace value must be not matched",
			args: args{
				matchNs: v1alpha1.Namespace{
					Values: []string{"test"},
				},
				ns:      "kblt-test",
				include: true,
			},
			want:  false,
			want1: "included namespace value",
		},
		{
			name: "exclude namespace value must be matched",
			args: args{
				matchNs: v1alpha1.Namespace{
					Values: []string{"test"},
				},
				ns:      "kblt-test",
				include: false,
			},
			want:  true,
			want1: "excluded namespace value",
		},
		{
			name: "exclude namespace value must be not matched",
			args: args{
				matchNs: v1alpha1.Namespace{
					Values: []string{"kblt-test"},
				},
				ns:      "kblt-test",
				include: false,
			},
			want:  false,
			want1: "excluded namespace value",
		},
		{
			name: "exclude namespace regex must be not matched",
			args: args{
				matchNs: v1alpha1.Namespace{
					Regex: []string{"kblt-.*"},
				},
				ns:      "kblt-test",
				include: false,
			},
			want:  false,
			want1: "excluded namespace regex",
		},
		{
			name: "exclude namespace value must be not matched",
			args: args{
				matchNs: v1alpha1.Namespace{
					Regex: []string{".*-test"},
				},
				ns:      "kblt",
				include: false,
			},
			want:  true,
			want1: "excluded namespace regex",
		},
		{
			name: "exclude namespace invalid regex",
			args: args{
				matchNs: v1alpha1.Namespace{
					Regex: []string{"a(b"},
				},
				ns:      "kblt",
				include: false,
			},
			want:  false,
			want1: "namespace invalid regex",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			got, got1 := doesNsNamesMatch(tt.args.matchNs, tt.args.ns, tt.args.include)
			if got != tt.want {
				t.Errorf("doesNsNamesMatch() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("doesNsNamesMatch() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_doesObjectSectionMatches(t *testing.T) {
	type args struct {
		obj       map[string]interface{}
		matchObjs []v1alpha1.Obj
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		{
			name: "Empty Match",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
				},
				matchObjs: nil,
			},
			want:  false,
			want1: "match conditions doesn't have enough of arguments",
		},
		{
			name: "MustExist Exist",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "metadata",
						Condition: mustExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "MustExist NotExist",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "test",
						Condition: mustExist,
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "MustNotExist NotExist",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "test",
						Condition: mustNotExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "MustNotExist Exist",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "metadata",
						Condition: mustNotExist,
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "Wrong path",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]string{
							{
								"name":  "istio-proxy",
								"value": "val",
							},
						},
						"generateName": "generateName-obj",
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "spec.containers[?@name == `istio-proxy`]",
						Condition: mustExist,
					},
				},
			},
			want:  false,
			want1: "match conditions jmespath processing error",
		},
		{
			name: "slice matched",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]string{
							{
								"name":  "istio-proxy",
								"value": "match",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "spec.containers[? @.name == 'test1']",
						Condition: mustExist,
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "slice not matched",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]string{
							{
								"name":  "istio-proxy",
								"value": "match",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "spec.containers[? @.name == 'test1']",
						Condition: mustNotExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "boolean result",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]interface{}{ //[]map[string]string - wrong type
							{
								"name":  "istio-proxy",
								"value": "match",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test3",
								"image": "val",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "contains(spec.containers[? @.name==`test3`].image, `val`)",
						Condition: mustExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "boolean result not matched",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]interface{}{ //[]map[string]string - wrong type
							{
								"name":  "istio-proxy",
								"value": "match",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test3",
								"image": "val",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "contains(spec.containers[? @.name==`test3`].image, `not mathced`)",
						Condition: mustExist,
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "boolean result not exist true",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]interface{}{ //[]map[string]string - wrong type
							{
								"name":  "istio-proxy",
								"value": "match",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test3",
								"image": "val",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "contains(spec.containers[? @.name==`test3`].image, `not matched`)",
						Condition: mustNotExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "boolean result not exist true",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
					"spec": map[string]interface{}{
						"containers": []map[string]interface{}{ //[]map[string]string - wrong type
							{
								"name":  "istio-proxy",
								"value": "match",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test3",
								"image": "val",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "contains(spec.containers[? @.name==`test3`].image, `val`)",
						Condition: mustNotExist,
					},
				},
			},
			want:  false,
			want1: "match conditions",
		},
		{
			name: "matched result is a number",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
						"value":        3.14,
					},
					"spec": map[string]interface{}{
						"containers": []map[string]interface{}{ //[]map[string]string - wrong type
							{
								"name":  "istio-proxy",
								"value": "3.14",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test3",
								"image": "val",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "metadata.value",
						Condition: mustExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "matched result is a string",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
						"value":        3.14,
					},
					"spec": map[string]interface{}{
						"containers": []map[string]interface{}{ //[]map[string]string - wrong type
							{
								"name":  "istio-proxy",
								"value": "3.14",
								"test":  "val",
							},
							{
								"name":  "test3",
								"value": "val",
								"match": "test3",
								"image": "val",
							},
						},
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "metadata.name",
						Condition: mustExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
		{
			name: "matched result is a map",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
						"value":        3.14,
					},
				},
				matchObjs: []v1alpha1.Obj{
					{
						Path:      "metadata",
						Condition: mustExist,
					},
				},
			},
			want:  true,
			want1: "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			got, got1 := doesObjectSectionMatches(tt.args.obj, tt.args.matchObjs)
			if got != tt.want {
				t.Errorf("doesObjectSectionMatches() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("doesObjectSectionMatches() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_doesOperationsMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "Empty match section",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "Must be Matched",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Create,
				},
				match: v1alpha1.Match{
					Operations: []string{"CREATE"},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "Must be not Matched",
			args: args{
				fields: common.ARFields{
					Operation: v1beta1.Delete,
				},
				match: v1alpha1.Match{
					Operations: []string{"CREATE"},
				},
			},
			wantMatches: false,
			wantReason:  "request.operation",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotMatches, gotReason := doesOperationsMatches(context.Background(), tt.args.fields, tt.args.match)
			if gotMatches != tt.wantMatches {
				t.Errorf("doesOperationsMatches() gotMatches = %v, want %v", gotMatches, tt.wantMatches)
			}
			if gotReason != tt.wantReason {
				t.Errorf("doesOperationsMatches() gotReason = %v, want %v", gotReason, tt.wantReason)
			}
		})
	}
}

func Test_doesScopeMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "Namespaced match",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
				},
				match: v1alpha1.Match{
					Scope: scopeNamespaced,
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "Namespaced not match",
			args: args{
				fields: common.ARFields{},
				match: v1alpha1.Match{
					Scope: scopeNamespaced,
				},
			},
			wantMatches: false,
			wantReason:  "request.namespace",
		},
		{
			name: "Cluster match",
			args: args{
				fields: common.ARFields{},
				match: v1alpha1.Match{
					Scope: scopeCluster,
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "Cluster not match",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
				},
				match: v1alpha1.Match{
					Scope: scopeCluster,
				},
			},
			wantMatches: false,
			wantReason:  "request.namespace",
		},
		{
			name: "Any match",
			args: args{
				fields: common.ARFields{},
				match: v1alpha1.Match{
					Scope: scopeAny,
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "Empty match section",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
				},
				match: v1alpha1.Match{},
			},
			wantMatches: true,
			wantReason:  "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotMatches, gotReason := doesScopeMatches(context.Background(), tt.args.fields, tt.args.match)
			if gotMatches != tt.wantMatches {
				t.Errorf("doesScopeMatches() gotMatches = %v, want %v", gotMatches, tt.wantMatches)
			}
			if gotReason != tt.wantReason {
				t.Errorf("doesScopeMatches() gotReason = %v, want %v", gotReason, tt.wantReason)
			}
		})
	}
}

func Test_doesUserInfoMatches(t *testing.T) {
	type args struct {
		fields common.ARFields
		match  v1alpha1.Match
	}
	tests := []struct {
		name        string
		args        args
		wantMatches bool
		wantReason  string
	}{
		{
			name: "Exist user value",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{
					UserInfo: v1alpha1.UserInfo{
						Username: "user-for-test",
					},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "NotExist user value",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{
					UserInfo: v1alpha1.UserInfo{
						Username: "test-user",
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.userInfo.username",
		},
		{
			name: "Exist user regex",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{
					UserInfo: v1alpha1.UserInfo{
						UsernameRegex: ".*-for-.*",
					},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "NotExist user regex",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{
					UserInfo: v1alpha1.UserInfo{
						UsernameRegex: "ololol-.*",
					},
				},
			},
			wantMatches: false,
			wantReason:  "request.userInfo.username",
		},
		{
			name: "Wrong user regex",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{
					UserInfo: v1alpha1.UserInfo{
						UsernameRegex: `a(b`,
					},
				},
			},
			wantMatches: false,
			wantReason:  "wrong match.userInfo.usernameRegex: error parsing regexp: missing closing ): `a(b`",
		},
		{
			name: "Empty Match section",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{
					UserInfo: v1alpha1.UserInfo{
						Username:      "",
						UsernameRegex: "",
					},
				},
			},
			wantMatches: true,
			wantReason:  "",
		},
		{
			name: "Empty Match section",
			args: args{
				fields: common.ARFields{
					Namespace: "kblt",
					UserInfo: common.ARUserInfo{
						Username: "user-for-test",
					},
					Operation: "CREATE",
				},
				match: v1alpha1.Match{},
			},
			wantMatches: true,
			wantReason:  "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotMatches, gotReason := doesUserInfoMatches(context.Background(), tt.args.fields, tt.args.match)
			if gotMatches != tt.wantMatches {
				t.Errorf("doesUserInfoMatches() gotMatches = %v, want %v", gotMatches, tt.wantMatches)
			}
			if gotReason != tt.wantReason {
				t.Errorf("doesUserInfoMatches() gotReason = %v, want %v", gotReason, tt.wantReason)
			}
		})
	}
}

func Test_findNonRegexNsMatch(t *testing.T) {
	type args struct {
		ns       string
		matchVal []string
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		{
			name: "Exist value",
			args: args{
				ns:       "kblt",
				matchVal: []string{"kblt", "test"},
			},
			want:  true,
			want1: "namespace value",
		},
		{
			name: "NotExist value",
			args: args{
				ns:       "kblt",
				matchVal: []string{"test"},
			},
			want:  false,
			want1: "namespace value",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1 := findNonRegexNsMatch(tt.args.ns, tt.args.matchVal)
			if got != tt.want {
				t.Errorf("findNonRegexNsMatch() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("findNonRegexNsMatch() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_findRegexNsMatch(t *testing.T) {
	type args struct {
		ns       string
		matchVal []string
	}
	tests := []struct {
		name  string
		args  args
		want  bool
		want1 string
	}{
		{
			name: "Exist value",
			args: args{
				ns:       "kblt",
				matchVal: []string{".*blt"},
			},
			want:  true,
			want1: "namespace regex",
		},
		{
			name: "NotExist value",
			args: args{
				ns:       "kblt",
				matchVal: []string{".*-test"},
			},
			want:  false,
			want1: "namespace regex",
		},
		{
			name: "Wrong regex",
			args: args{
				ns:       "kblt",
				matchVal: []string{`a(b`},
			},
			want:  false,
			want1: "namespace invalid regex",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			got, got1 := findRegexNsMatch(tt.args.ns, tt.args.matchVal)
			if got != tt.want {
				t.Errorf("findRegexNsMatch() got = %v, want %v", got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("findRegexNsMatch() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func Test_getAdmissionReviewResourceName(t *testing.T) {
	type args struct {
		obj map[string]interface{}
	}
	tests := []struct {
		name     string
		args     args
		wantName string
	}{
		{
			name: "name",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"name":         "name-obj",
						"generateName": "generateName-obj",
					},
				},
			},
			wantName: "name-obj",
		},
		{
			name: "generateName",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"generateName": "generateName-obj",
					},
				},
			},
			wantName: "generateName-obj",
		},
		{
			name: "empty name",
			args: args{
				obj: map[string]interface{}{
					"metadata": map[string]interface{}{
						"whatsYourName": "zhoskiy",
					},
				},
			},
			wantName: "config_name_was_not_found",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if gotName := getAdmissionReviewResourceName(tt.args.obj); gotName != tt.wantName {
				t.Errorf("getAdmissionReviewResourceName() = %v, want %v", gotName, tt.wantName)
			}
		})
	}
}

func Test_getObjectToValidate(t *testing.T) {
	type args struct {
		data common.ARFields
	}
	tests := []struct {
		name string
		args args
		want map[string]interface{}
	}{
		{
			name: "Create",
			args: args{data: common.ARFields{
				Operation: v1beta1.Create,
				OldObject: map[string]interface{}{"old": -1},
				Object:    map[string]interface{}{"new": 1},
			}},
			want: map[string]interface{}{"new": 1},
		},
		{
			name: "Update",
			args: args{data: common.ARFields{
				Operation: v1beta1.Update,
				OldObject: map[string]interface{}{"old": -1},
				Object:    map[string]interface{}{"new": 1},
			}},
			want: map[string]interface{}{"new": 1},
		},
		{
			name: "Delete",
			args: args{data: common.ARFields{
				Operation: v1beta1.Delete,
				OldObject: map[string]interface{}{"old": -1},
				Object:    map[string]interface{}{"new": 1},
			}},
			want: map[string]interface{}{"old": -1},
		},
		{
			name: "Wrong",
			args: args{data: common.ARFields{
				Operation: v1beta1.Operation("wrong"),
				OldObject: map[string]interface{}{"old": -1},
				Object:    map[string]interface{}{"new": 1},
			}},
			want: nil,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			assert.Equalf(t, tt.want, getObjectToValidate(context.Background(), tt.args.data), "getObjectToValidate(%v)", tt.args.data)
		})
	}
}
